package com.jd.dlc.lock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @program: dlc
 * @ClassName KeyLocker
 * @description:
 * @author: sizegang
 * @create: 2022-05-02
 **/
public class KeyLocker implements com.jd.dlc.lock.Lock {

    Logger log = LoggerFactory.getLogger(KeyLocker.class);

    public Map<String, Lock> lockMap = new ConcurrentHashMap<>();

    public boolean tryLock(String key) {
        if (Objects.isNull(key)) {
            throw new NullPointerException();
        }
        if (lockMap.containsKey(key)) {
            ReentrantLock lock = (ReentrantLock) lockMap.get(key);
            /**
             * Determine whether the current thread already holds the lock, if so, return true, otherwise false
             * reentrant lock 可重入
             */
            return lock.isHeldByCurrentThread();
        } else {
            Lock lock = new ReentrantLock();
            if (lock.tryLock()) {
                /**
                 * double judgment
                 */
                if (lockMap.containsKey(key)) {
                    return false;
                }
                lockMap.put(key, lock);
                log.info("{} keyLocker tryLock  key is {}", Thread.currentThread().getName(), key);
                return true;
            }
            return false;
        }
    }

    public boolean tryRelease(String t) {
        if (Objects.nonNull(t) && lockMap.containsKey(t)) {
            /**
             * First determine whether the lock is held by the current thread, and the holder can unlock it,
             * otherwise a java.lang.IllegalMonitorStateException will be thrown
             */
            ReentrantLock lock = (ReentrantLock) lockMap.get(t);
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                lockMap.remove(t);
                log.info("{} keyLocker tryRelease {}", Thread.currentThread().getName(), t);
                return true;
            }
        }
        return false;
    }
}